/*

    particleswarm.cpp - implementation of the particleswarm class
    Author Julio Agustin Barrera Mendoza
    Copyright (C) 2009 CINVESTAV-IPN.

    This program is free software; you can redistribute it and/or modify it 
    under the terms of the GNU General Public License as published by 
    the Free Software Foundation; either version 2 of the License, 
    or (at your option) any later version.

    This program is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
    General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to 
    the Free Software Foundation, Inc., 59 Temple Place, 
    Suite 330, Boston, MA 02111-1307 USA

*/

#include "particleswarm.h"

// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::ParticleSwarm
// ------------------------------------------------------------------

ParticleSwarm::ParticleSwarm()
{
  particles = 0;
  c1 = 2.0;
  c2 = 2.0;
  
  function = dummy;
  functionName = "Dummy";
}

// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::~ParticleSwarm
// ------------------------------------------------------------------

ParticleSwarm::~ParticleSwarm()
{
}

// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::loadOptions
// ------------------------------------------------------------------

void ParticleSwarm::loadOptions(const string &filename)
{
  unsigned int i;
  ifstream input;
  string line;
  vector<string> contents;
  
  input.open(filename.c_str());

  while (getline(input, line))
    contents.push_back(line);

  input.close();

  for (i = 0; i < contents.size(); i++)
    {
      vector<string> tokens = tokenize(contents[i]);
            
      if (tokens[0] == "particles")
        {
          istringstream inputString(tokens[1]);
          
          inputString >> particles;
        }
      else if (tokens[0] == "iterations")
        {
          istringstream inputString(tokens[1]);
          
          inputString >> iterations;
        }
      else if (tokens[0] == "c1")
        {
          istringstream inputString(tokens[1]);
          
          inputString >> c1;
        }
      else if (tokens[0] == "c2")
        {
          istringstream inputString(tokens[1]);
          
          inputString >> c2;
        }
      else if (tokens[0] == "range")
        {
          unsigned int repeat, j;
          long double minimum, maximum;
          istringstream inputString;
          vector<string>::iterator name;
          
          name = find(tokens.begin(), tokens.end(), "repeat");
          inputString.clear();
          inputString.str(*(name + 1));
          inputString >> repeat;
          
          name = find(tokens.begin(), tokens.end(), "minimum");
          inputString.clear();
          inputString.str(*(name + 1));
          inputString >> minimum;
          
          name = find(tokens.begin(), tokens.end(), "maximum");
          inputString.clear();
          inputString.str(*(name + 1));
          inputString >> maximum;

          for (j = 0; j < repeat; j++)
            {
              Range range;
              
              range.Minimum() = minimum;
              range.Maximum() = maximum;
              
              ranges.push_back(range);
            }
        }
      else if (tokens[0] == "function")
        {
          if (tokens[1] == "sphere")
            {
              functionName = "Sphere";
              function = sphere;
            }
          else if (tokens[1] == "schwefelone")
            {
              functionName = "Schwefel simple";
              function = schwefelOne;
            }
          else if (tokens[1] == "rosenbrock")
            {
              functionName = "Rosenbrock";
              function = rosenbrock;
            }
          else if (tokens[1] == "schwefeltwo")
            {
              functionName = "Schwefel sine";
              function = schwefelTwo;
            }
          else if (tokens[1] == "rastrigin")
            {
              functionName = "Rastrigin";
              function = rastrigin;
            }
          else if (tokens[1] == "ackley")
            {
              functionName = "Ackley";
              function = ackley;
            }
          else if (tokens[1] == "griewank")
            {
              functionName = "Griewank";
              function = griewank;
            }
        }
      else if (tokens[0] == "seedsource")
        {        
          if (tokens.size() > 2)
            random.setSeedSource(tokens[1], tokens[2]);
          else
            random.setSeedSource(tokens[1]);
        }
      else if (tokens[0] == "randomdriver")
        {        
          if (tokens[1] == "drand48")
            random.useDrand48();
          else if (tokens[1] == "mt")
            random.useMt();
        }
    }

  random.seed();
}

// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::printOptions
// ------------------------------------------------------------------

void ParticleSwarm::printOptions(void)
{
  unsigned int i;
 
  cerr << "Options" << endl;
  cerr << "Iterations   : " << iterations << endl;
  cerr << "Particles    : " << particles << endl;
  cerr << "C1           : " << c1 << endl;
  cerr << "C2           : " << c2 << endl;
  cerr << "Seed source  : " << random.SeedSource() << endl;
  cerr << "Random driver: " << random.Driver() << endl;
  cerr << "Function     : " << functionName << endl;
  cerr << "Ranges" << endl;
  
  for (i = 0; i < ranges.size(); i++)
    cerr << ranges[i].Minimum() << " " << ranges[i].Maximum() << endl;
}

// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::generateSwarm
// ------------------------------------------------------------------

void ParticleSwarm::generateSwarm(void)
{
  unsigned int i, j, variables = ranges.size();
  Array zeroVelocity(variables);
  
  for (i = 0; i < particles; i++)
    {
      Particle particle;
      
      for (j = 0; j < variables; j++)
        particle.Position() << random.Uniform() * ranges[j].Length() + ranges[j].Minimum();
      
      particle.Value() = function(particle.Position());
      particle.Velocity() = zeroVelocity;

      particle.BestValue() = particle.Value();
      particle.BestPosition() = particle.Position();

      swarm.push_back(particle);
    }
}

// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::printSwarm
// ------------------------------------------------------------------

void ParticleSwarm::printSwarm(void)
{
  unsigned int i, j, variables = ranges.size();
  
  for (i = 0; i < particles; i++)
    {
      for (j = 0; j < variables; j++)
        cerr << swarm[i].Position()[j] << " ";
      cerr << endl;
    }
}

// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::printParticle
// ------------------------------------------------------------------

void ParticleSwarm::printParticle(unsigned int index)
{
  cout << swarm[index].BestValue() << endl;
}

// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::updateVelocity
// ------------------------------------------------------------------

void ParticleSwarm::updateVelocity(void)
{
  unsigned int i, indexBest;
  Array velocity;

  indexBest = findGlobalBest();

  for (i = 0; i < particles; i++)
    {
      velocity =   c1 * random.Uniform() * (swarm[i].BestPosition() - swarm[i].Position())
                 + c2 * random.Uniform() * (swarm[indexBest].BestPosition() - swarm[i].Position());

      swarm[i].Velocity() += velocity;
    }
}

// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::updatePosition
// ------------------------------------------------------------------

void ParticleSwarm::updatePosition(void)
{
  unsigned int i;
  
  for (i = 0; i < particles; i++)
    swarm[i].Position() += swarm[i].Velocity();
}

// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::updatePersonalBest
// ------------------------------------------------------------------

void ParticleSwarm::updatePersonalBest(void)
{
  unsigned int i;
  
  for (i = 0; i < particles; i++)
    if (swarm[i].Value() < swarm[i].BestValue())
      {
        swarm[i].BestValue() = swarm[i].Value();
        swarm[i].BestPosition() = swarm[i].Position();
      }
}
    
// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::evaluateParticles
// ------------------------------------------------------------------

void ParticleSwarm::evaluateParticles(void)
{
  unsigned int i;
  
  for (i = 0; i < particles; i++)
    swarm[i].Value() = function(swarm[i].Position());
}

// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::findGlobalBest
// ------------------------------------------------------------------

unsigned int ParticleSwarm::findGlobalBest(void)
{
  unsigned int i, indexBest = 0;

  for (i = 0; i < particles; i++)
    if (swarm[i].BestValue() < swarm[indexBest].BestValue())
      indexBest = i;

  return indexBest;
}

// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::clipVelocity
// ------------------------------------------------------------------

void ParticleSwarm::clipVelocity(void)
{
  unsigned int i, j, variables = ranges.size();
  
  for (i = 0; i < particles; i++)
    for (j = 0; j < variables; j++)
      {
        if (swarm[i].Velocity()[j] < ranges[j].Minimum())
          swarm[i].Velocity()[j] = ranges[j].Minimum();
        if (swarm[i].Velocity()[j] > ranges[j].Maximum())
          swarm[i].Velocity()[j] = ranges[j].Maximum();
      }
}

// ------------------------------------------------------------------
// FUNCTION ParticleSwarm::clipPosition
// ------------------------------------------------------------------

void ParticleSwarm::clipPosition(void)
{
  unsigned int i, j, variables = ranges.size();
  
  for (i = 0; i < particles; i++)
    for (j= 0; j < variables; j++)
      {
        if (swarm[i].Position()[j] < ranges[j].Minimum())
          swarm[i].Position()[j] = ranges[j].Minimum();
        if (swarm[i].Position()[j] > ranges[j].Maximum())
          swarm[i].Position()[j] = ranges[j].Maximum();
      }
}

